{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在Python中，定义一个函数要使用def语句，依次写出函数名、括号、括号中的参数和冒号:，然后，在缩进块中编写函数体，函数的返回值用return语句返回"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6\n"
     ]
    }
   ],
   "source": [
    "# 案例\n",
    "# 自定义一个求绝对值的my_ab函数\n",
    "def my_abs(x):\n",
    "    if x >= 0:\n",
    "        return x\n",
    "    else:\n",
    "        return -x\n",
    "print(my_abs(-6))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 参数检查\n",
    "- 调用函数时，如果参数个数不对，Python解释器会自动检查出来，并抛出TypeError：\n",
    "- 但是如果参数类型不对，Python解释器就无法帮我们检查。试试my_abs和内置函数abs的差别："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "'>=' not supported between instances of 'str' and 'int'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-3-91ba0acb57cc>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mmy_abs\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'a'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-1-0ee3c5bfad8a>\u001b[0m in \u001b[0;36mmy_abs\u001b[1;34m(x)\u001b[0m\n\u001b[0;32m      2\u001b[0m \u001b[1;31m# 自定义一个求绝对值的my_ab函数\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      3\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mmy_abs\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 4\u001b[1;33m     \u001b[1;32mif\u001b[0m \u001b[0mx\u001b[0m \u001b[1;33m>=\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      5\u001b[0m         \u001b[1;32mreturn\u001b[0m \u001b[0mx\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      6\u001b[0m     \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mTypeError\u001b[0m: '>=' not supported between instances of 'str' and 'int'"
     ]
    }
   ],
   "source": [
    "my_abs('a')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "bad operand type for abs(): 'str'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-4-f2001f88707b>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mabs\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'a'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: bad operand type for abs(): 'str'"
     ]
    }
   ],
   "source": [
    "abs('a')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 修改一下my_abs的定义，对参数类型做检查\n",
    "def my_abs(x):\n",
    "    if not isinstance(x, (int, float)):# 检查数据类型\n",
    "        raise TypeError('bad operand type')\n",
    "    if x >= 0:\n",
    "        return x\n",
    "    else:\n",
    "        return -x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "bad operand type",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-6-91ba0acb57cc>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mmy_abs\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'a'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-5-eebcfffa2231>\u001b[0m in \u001b[0;36mmy_abs\u001b[1;34m(x)\u001b[0m\n\u001b[0;32m      2\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mmy_abs\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      3\u001b[0m     \u001b[1;32mif\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[0misinstance\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m(\u001b[0m\u001b[0mint\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mfloat\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;31m# 检查数据类型\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 4\u001b[1;33m         \u001b[1;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'bad operand type'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      5\u001b[0m     \u001b[1;32mif\u001b[0m \u001b[0mx\u001b[0m \u001b[1;33m>=\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      6\u001b[0m         \u001b[1;32mreturn\u001b[0m \u001b[0mx\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mTypeError\u001b[0m: bad operand type"
     ]
    }
   ],
   "source": [
    "my_abs('a')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 返回多个值\n",
    "函数可以返回多个值吗？答案是肯定的。\n",
    "\n",
    "比如在游戏中经常需要从一个点移动到另一个点，给出坐标、位移和角度，就可以计算出新的坐标："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math # 导入math包，并允许后续代码引用math包里的sin、cos等函数。\n",
    "\n",
    "def move(x, y, step, angle=0):\n",
    "    nx = x + step * math.cos(angle)\n",
    "    ny = y - step * math.sin(angle)\n",
    "    return nx, ny"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "151.96152422706632 70.0\n"
     ]
    }
   ],
   "source": [
    "x, y = move(100, 100, 60, math.pi / 6)\n",
    "print(x, y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "其实这只是一种假象，Python函数返回的仍然是单一值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(151.96152422706632, 70.0)\n"
     ]
    }
   ],
   "source": [
    "r = move(100, 100, 60, math.pi / 6)\n",
    "print(r)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "原来返回值是一个tuple！但是，在语法上，返回一个tuple可以省略括号，而多个变量可以同时接收一个tuple，按位置赋给对应的值，所以，Python的函数返回多值其实就是返回一个tuple，但写起来更方便。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 练习\n",
    "请定义一个函数quadratic(a, b, c)，接收3个参数，返回一元二次方程ax^2 + bx + c = 0 的两个解。\n",
    "计算平方根可以调用math.sqrt()函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3.0"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import math \n",
    "math.sqrt(9)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "def quadratic(a, b, c):\n",
    "    if not isinstance(a,(int,float)) and isinstance(b,(int,float)) and isinstance(c,(int,float)):\n",
    "        raise TypeError('bad operand error')\n",
    "    x1 = (math.sqrt(b**2 - 4*a*c)-b) / (2*a)\n",
    "    x2 = (-math.sqrt(b**2 - 4*a*c)-b) / (2*a)\n",
    "    return x1, x2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(-0.5, -1.0)"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "quadratic(2, 3, 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "quadratic(2, 3, 1) = (-0.5, -1.0)\n",
      "quadratic(1, 3, -4) = (1.0, -4.0)\n",
      "测试成功\n"
     ]
    }
   ],
   "source": [
    "# 测试:\n",
    "print('quadratic(2, 3, 1) =', quadratic(2, 3, 1))\n",
    "print('quadratic(1, 3, -4) =', quadratic(1, 3, -4))\n",
    "\n",
    "if quadratic(2, 3, 1) != (-0.5, -1.0):\n",
    "    print('测试失败')\n",
    "elif quadratic(1, 3, -4) != (1.0, -4.0):\n",
    "    print('测试失败')\n",
    "else:\n",
    "    print('测试成功')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on built-in function isinstance in module builtins:\n",
      "\n",
      "isinstance(obj, class_or_tuple, /)\n",
      "    Return whether an object is an instance of a class or of a subclass thereof.\n",
      "    \n",
      "    A tuple, as in ``isinstance(x, (A, B, ...))``, may be given as the target to\n",
      "    check against. This is equivalent to ``isinstance(x, A) or isinstance(x, B)\n",
      "    or ...`` etc.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(isinstance)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
